package cn.chiship.sdk.framework.velocity;

import cn.chiship.sdk.core.util.JdbcUtil;
import cn.chiship.sdk.core.util.PlatformUtil;
import cn.chiship.sdk.core.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 代码生成类.
 *
 * @author lijian
 */
public class MybatisGeneratorTools {

	private static final Logger LOGGER = LoggerFactory.getLogger(MybatisGeneratorTools.class);

	/**
	 * generatorConfig模板路径
	 */
	private static String generatorConfigVm = "/template/generatorConfig.vm";

	/**
	 * Service模板路径
	 */
	private static String serviceVm = "/template/Service.vm";

	/**
	 * ServiceImpl模板路径
	 */
	private static String serviceImplVm = "/template/ServiceImpl.vm";

	/**
	 * Controller模板路径
	 */
	private static String controllerVm = "/template/Controller.vm";

	/**
	 * Pojo.Dto模板路径
	 */
	private static String dtoVm = "/template/Dto.vm";

	/**
	 * Vue模板路径
	 */
	private static String webVueVm = "/template/Web.Vue.vm";

	/**
	 * Vue Service模板路径
	 */
	private static String webServiceVm = "/template/Web.Service.vm";

	private MybatisGeneratorTools() {
	}

	/**
	 * 根据模板生成generatorConfig.xml文件
	 * @param jdbcDriver 驱动路径
	 * @param jdbcUrl 链接
	 * @param jdbcUsername 帐号
	 * @param jdbcPassword 密码
	 * @param database 数据库
	 * @param tablePrefix 表前缀
	 * @param packageName 包名
	 */
	public static void generator(String jdbcDriver, String jdbcUrl, String jdbcUsername, String jdbcPassword,
			String database, String tablePrefix, String packageName, Map<String, String> lastInsertIdTables)
			throws Exception {
		String basePath = MybatisGeneratorTools.class.getResource("/").getPath().replace("/target/classes/", "");
		if (!PlatformUtil.isLinux()) {
			generatorConfigVm = MybatisGeneratorTools.class.getResource(generatorConfigVm).getPath().replaceFirst("/",
					"");
			serviceVm = MybatisGeneratorTools.class.getResource(serviceVm).getPath().replaceFirst("/", "");
			serviceImplVm = MybatisGeneratorTools.class.getResource(serviceImplVm).getPath().replaceFirst("/", "");
			controllerVm = MybatisGeneratorTools.class.getResource(controllerVm).getPath().replaceFirst("/", "");
			dtoVm = MybatisGeneratorTools.class.getResource(dtoVm).getPath().replaceFirst("/", "");
			webVueVm = MybatisGeneratorTools.class.getResource(webVueVm).getPath().replaceFirst("/", "");
			webServiceVm = MybatisGeneratorTools.class.getResource(webServiceVm).getPath().replaceFirst("/", "");

			basePath = basePath.replaceFirst("/", "");
		}
		else {
			generatorConfigVm = MybatisGeneratorTools.class.getResource(generatorConfigVm).getPath();
			serviceVm = MybatisGeneratorTools.class.getResource(serviceVm).getPath();
			serviceImplVm = MybatisGeneratorTools.class.getResource(serviceImplVm).getPath();
			controllerVm = MybatisGeneratorTools.class.getResource(controllerVm).getPath();
			dtoVm = MybatisGeneratorTools.class.getResource(dtoVm).getPath();
			webVueVm = MybatisGeneratorTools.class.getResource(webVueVm).getPath();
			webServiceVm = MybatisGeneratorTools.class.getResource(webServiceVm).getPath();

		}

		String generatorConfigXml = MybatisGeneratorTools.class.getResource("/").getPath().replace("/target/classes/",
				"") + "/src/main/resources/generatorConfig.xml";
		deleteDir(new File(generatorConfigXml));
		LOGGER.info("========== 开始生成generatorConfig.xml文件 ==========");
		List<Map<String, Object>> tables = new ArrayList<>();
		try {
			VelocityContext context = new VelocityContext();
			Map<String, Object> table;

			/**
			 * 查询定制前缀项目的所有表
			 */
			JdbcUtil jdbcUtil = new JdbcUtil(jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword);
			List<Map<String, Object>> result = jdbcUtil.selectTablesByDatabase(database, tablePrefix);
			jdbcUtil.release();

			for (Map<String, Object> map : result) {
				String tableName = StringUtil.getString(map.get("name"));
				String tableComment = StringUtil.getString(map.get("comment"));

				LOGGER.info(tableName);
				table = new HashMap<>(2);
				table.put("table_name", tableName);
				table.put("table_comment", tableComment);
				table.put("model_name", StringUtil.lineToPascal(tableName));

				if (!StringUtil.isNullOrEmpty(tableName)) {

					JdbcUtil jdbcUtilCol = new JdbcUtil(jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword);
					List<Map<String, Object>> columnResults = jdbcUtilCol.selectColumnsByTable(database, tableName);
					jdbcUtilCol.release();
					List<JSONObject> columns = new LinkedList<>();
					for (Map<String, Object> item : columnResults) {
						String columnName = StringUtil.getString(item.get("column_name"));
						String columnComment = StringUtil.getString(item.get("column_comment"));

						JSONObject columnJson = new JSONObject();
						columnJson.put("name", StringUtil.toLowerCaseFirstOne(StringUtil.lineToPascal(columnName)));
						columnJson.put("comment",
								StringUtil.toLowerCaseFirstOne(StringUtil.lineToPascal(columnComment)));

						columns.add(columnJson);
					}
					table.put("columns", columns);
					jdbcUtilCol.release();
				}

				tables.add(table);
			}
			jdbcUtil.release();
			String targetProject = basePath;
			String targetProjectSqlMap = basePath;
			context.put("tables", tables);
			context.put("generator_javaModelGenerator_targetPackage", packageName + ".entity");
			context.put("generator_sqlMapGenerator_targetPackage", packageName + ".mapper");
			context.put("generator_javaClientGenerator_targetPackage", packageName + ".mapper");
			context.put("targetProject", targetProject);
			context.put("targetProject_sqlMap", targetProjectSqlMap);
			context.put("generator_jdbc_password", jdbcPassword);
			context.put("last_insert_id_tables", lastInsertIdTables);
			VelocityUtil.generate(generatorConfigVm, generatorConfigXml, context);
			for (int i = 0; i < tables.size(); i++) {
				String model = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_name")));
				deleteDir(new File(targetProjectSqlMap + "/src/main/resources/" + packageName.replaceAll("\\.", "/")
						+ "/mapper/" + model + "Mapper.xml"));
			}
		}
		catch (Exception e) {
			LOGGER.error("发生异常", e);
		}
		LOGGER.info("========== 结束生成generatorConfig.xml文件 ==========");

		LOGGER.info("========== 开始运行MybatisGenerator ==========");
		List<String> warnings = new ArrayList<>();
		File configFile = new File(generatorConfigXml);
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(true);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
		myBatisGenerator.generate(null);
		for (String warning : warnings) {
			LOGGER.info(warning);
		}
		LOGGER.info("========== 结束运行MybatisGenerator ==========");

		String ctime = new SimpleDateFormat("yyyy/M/d").format(new Date());

		generatorServices(basePath, packageName, tables, ctime);
		generatorController(basePath, packageName, tables, ctime);
		generatorDto(basePath, packageName, tables, ctime);
		generatorVue(basePath, packageName, tables, ctime);

	}

	/**
	 * 生成业务逻辑
	 * @param basePath
	 * @param packageName
	 * @param tables
	 * @param ctime
	 * @throws Exception
	 */
	private static void generatorServices(String basePath, String packageName, List<Map<String, Object>> tables,
			String ctime) throws Exception {
		LOGGER.info("========== 开始生成Service ==========");
		String servicePath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/service";
		String serviceImplPath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/service/impl";
		/**
		 * 如果文件夹不存在，创建文件夹
		 */
		FileUtils.forceMkdir(new File(servicePath));
		FileUtils.forceMkdir(new File(serviceImplPath));
		for (int i = 0; i < tables.size(); i++) {
			String model = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_name")));
			String modelName = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_comment")));

			String service = servicePath + "/" + model + "Service.java";
			String serviceImpl = serviceImplPath + "/" + model + "ServiceImpl.java";
			VelocityContext context = commonContext(packageName, model, modelName, ctime);
			File serviceFile = new File(service);
			if (!serviceFile.exists()) {
				VelocityUtil.generate(serviceVm, service, context);
				LOGGER.info("service路径：{}", service);

			}
			File serviceImplFile = new File(serviceImpl);
			if (!serviceImplFile.exists()) {
				context.put("mapper", StringUtil.toLowerCaseFirstOne(model));
				VelocityUtil.generate(serviceImplVm, serviceImpl, context);
				LOGGER.info("serviceImpl路径：{}", serviceImpl);

			}
		}
		LOGGER.info("========== 结束生成Service ==========");
	}

	/**
	 * 生成控制层
	 * @param basePath
	 * @param packageName
	 * @param tables
	 * @param ctime
	 * @throws Exception
	 */
	private static void generatorController(String basePath, String packageName, List<Map<String, Object>> tables,
			String ctime) throws Exception {
		LOGGER.info("========== 开始生成Controller ==========");
		String controllerPath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/controller";
		// 如果文件夹不存在，创建文件夹
		FileUtils.forceMkdir(new File(controllerPath));
		for (int i = 0; i < tables.size(); i++) {
			String model = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_name")));
			String modelName = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_comment")));
			String controller = controllerPath + "/" + model + "Controller.java";
			VelocityContext context = commonContext(packageName, model, modelName, ctime);
			File serviceFile = new File(controller);
			if (!serviceFile.exists()) {
				VelocityUtil.generate(controllerVm, controller, context);
				LOGGER.info("controller路径：{}", controller);

			}
		}
		LOGGER.info("========== 结束生成Controller ==========");

	}

	/**
	 * 生成数据传输对象 vo/dto
	 * @param basePath
	 * @param packageName
	 * @param tables
	 * @param ctime
	 * @throws Exception
	 */
	private static void generatorDto(String basePath, String packageName, List<Map<String, Object>> tables,
			String ctime) throws Exception {
		LOGGER.info("========== 开始生成Dto ==========");
		String dtoPath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/pojo/dto";
		/**
		 * 如果文件夹不存在，创建文件夹
		 */
		FileUtils.forceMkdir(new File(dtoPath));
		for (int i = 0; i < tables.size(); i++) {
			String model = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_name")));
			String modelName = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_comment")));
			String dto = dtoPath + "/" + model + "Dto.java";
			VelocityContext context = commonContext(packageName, model, modelName, ctime);
			File dtoFile = new File(dto);
			if (!dtoFile.exists()) {
				VelocityUtil.generate(dtoVm, dto, context);
				LOGGER.info("dto路径：{}", dto);
			}
		}
		LOGGER.info("========== 结束生成Dto ==========");
	}

	/**
	 * vue生成
	 * @param basePath
	 * @param packageName
	 * @param tables
	 * @param ctime
	 * @throws Exception
	 */
	private static void generatorVue(String basePath, String packageName, List<Map<String, Object>> tables,
			String ctime) throws Exception {
		LOGGER.info("========== 开始生成Vue相关 ==========");
		String webVuePath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/vue/vue";
		String webServicePath = basePath + "/src/main/java/" + packageName.replaceAll("\\.", "/") + "/vue/js";
		/**
		 * 如果文件夹不存在，创建文件夹
		 */
		FileUtils.forceMkdir(new File(webVuePath));
		FileUtils.forceMkdir(new File(webServicePath));
		for (int i = 0; i < tables.size(); i++) {
			String model = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_name")));
			String modelName = StringUtil.lineToPascal(StringUtil.getString(tables.get(i).get("table_comment")));
			List<JSONObject> columns = (List<JSONObject>) tables.get(i).get("columns");
			LOGGER.info(JSON.toJSONString(columns));
			VelocityContext context = commonContext(packageName, model, modelName, ctime);
			context.put("columns", columns);
			String vue = webVuePath + "/" + StringUtil.toLowerCaseFirstOne(model) + ".vue";
			File webVueFile = new File(vue);
			if (!webVueFile.exists()) {
				VelocityUtil.generate(webVueVm, vue, context);
				LOGGER.info("vue路径：{}", vue);
			}

			String vueService = webServicePath + "/" + model + "Service.js";
			File webServiceFile = new File(vueService);
			if (!webServiceFile.exists()) {
				VelocityUtil.generate(webServiceVm, vueService, context);
				LOGGER.info("vueService路径:{}", vueService);
			}
		}
		LOGGER.info("========== 结束生成Vue相关 ==========");
	}

	private static VelocityContext commonContext(String packageName, String model, String modelName, String ctime) {
		VelocityContext context = new VelocityContext();
		context.put("packageName", packageName);
		context.put("ClassName", model);
		context.put("className", StringUtil.toLowerCaseFirstOne(model));
		context.put("ctime", ctime);
		context.put("modelName", modelName);

		return context;
	}

	/**
	 * 递归删除非空文件夹
	 * @param dir
	 */
	public static void deleteDir(File dir) {
		LOGGER.error("删除路径：{}", dir.getPath());
		if (dir.isDirectory()) {
			File[] files = dir.listFiles();
			for (int i = 0; i < files.length; i++) {
				deleteDir(files[i]);
			}
		}
		if (!dir.delete()) {
			LOGGER.error("删除失败:{]", dir.getAbsolutePath());
		}
	}

}
